﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.Text;
using MobileSales.Server.Database;
using MobileSales.Common.Logger;

namespace MobileSales.Server.Services
{
    public class DocumentService : ServiceBase, IDocumentService
    {
        #region Document management

        #region GetDocument
        public Model.Documents.Document GetDocument(int documentId)
        {
            this.ValidateToken();
            Document dbDocument = entities.Documents.Where(x => x.DocumentId.Equals(documentId)).FirstOrDefault();
            if (dbDocument != null)
            {
                Model.Documents.Document document = new Model.Documents.Document();
                this.FillDocument(ref document, dbDocument);
                return document;
            }
            Logger.LogError("Document could not be found");
            return null;
        }
        #endregion

        #region GetDocumentList
        public IEnumerable<Model.Documents.Document> GetDocumentList()
        {
            this.ValidateToken();
            IEnumerable<Document> dbDocuments = entities.Documents;
            if (dbDocuments != null)
            {
                List<Model.Documents.Document> result = new List<Model.Documents.Document>();
                foreach (var dbDocument in dbDocuments)
                {
                    Model.Documents.Document document = new Model.Documents.Document();
                    this.FillDocument(ref document, dbDocument);
                    result.Add(document);
                }
                return result;
            }
            Logger.LogError("Document list could not be fetched");
            return null;
        }
        #endregion

        #region FillDocument
        private void FillDocument(ref Model.Documents.Document document, Document dbDocument)
        {
            document.ClientCity = dbDocument.ClientCity;
            document.ClientFax = dbDocument.ClientFax;
            document.ClientInfo = dbDocument.ClientInfo;
            document.ClientName = dbDocument.ClientName;
            document.ClientName2 = dbDocument.ClientName2;
            document.ClientNip = dbDocument.ClientNip;
            document.ClientPhone = dbDocument.ClientPhone;
            document.ClientStreet = dbDocument.ClientStreet;
            document.ClientZipcode = dbDocument.ClientZipcode;
            document.CreationDate = dbDocument.CreationDate;
            document.DocumentNo = int.Parse(dbDocument.DocumentNo);
            document.Id = dbDocument.DocumentId;
            document.IsPaid = dbDocument.IsPaid;
            document.OrderId = dbDocument.OrderId;
            document.PaymentDate = dbDocument.PaymentDate;
            document.PaymentMethod = GetPaymentMethod(dbDocument.PaymentMethodId);
            document.DocumentType = GetDocumentType(dbDocument.DocumentTypeId);
            document.Items = GetDocumentElementList(dbDocument.DocumentId);
        }
        #endregion

        #region StoreDocument
        public void StoreDocument(Model.Documents.Document document)
        {
            this.ValidateToken();
            Document dbDocument = null;
            if (document.Id != 0)
            {
                this.Raise("Documents cannot be edited");
                //dbDocument = entities.Documents.Where(x => x.DocumentId.Equals(document.Id)).FirstOrDefault();
                //if (dbDocument == null)
                //    this.Raise("Could not update document");
            }
            else
            {
                dbDocument = new Document();
            }


            DocumentType dbDocumentType = entities.DocumentTypes.Where(x => x.DocumentTypeId.Equals(document.DocumentType.Id)).FirstOrDefault();
            if (dbDocumentType == null)
            {
                this.Raise("Document type not found");
            }
            dbDocument.ClientCity = document.ClientCity;
            dbDocument.ClientFax = document.ClientFax;
            dbDocument.ClientInfo = document.ClientInfo;
            dbDocument.ClientName = document.ClientName;
            dbDocument.ClientName2 = document.ClientName2;
            dbDocument.ClientNip = document.ClientNip;
            dbDocument.ClientPhone = document.ClientPhone;
            dbDocument.ClientStreet = document.ClientStreet;
            dbDocument.ClientZipcode = document.ClientZipcode;
            dbDocument.CreationDate = document.CreationDate;
            dbDocument.DocumentNo = dbDocumentType.NextNo.ToString();
            dbDocument.CreatorId = this.GetEmployeeIdFromToken();
            dbDocument.IsPaid = document.IsPaid;
            dbDocument.OrderId = document.OrderId;
            dbDocument.PaymentDate = document.PaymentDate;
            dbDocument.DocumentTypeId = document.DocumentType.Id;
            dbDocument.PaymentMethodId = document.PaymentMethod.Id;
            dbDocumentType.NextNo += 1;
            if (document.Id == 0)
            {
                entities.Documents.AddObject(dbDocument);
            }
            entities.SaveChanges();

            int docId = dbDocument.DocumentId;
            IEnumerable<int> idsToAdd = document.Items.Where(x => x.Id != 0).Select(x => x.Id);
            IEnumerable<DocumentElement> toRemove = entities.DocumentElements.Where(x => x.DocumentId == docId && !idsToAdd.Contains(x.DocumentElementId));
            foreach (DocumentElement de in toRemove)
            {
                entities.DocumentElements.DeleteObject(de);
            }
            entities.SaveChanges();

            foreach (Model.Documents.DocumentElement el in document.Items)
            {
                el.DocumentId = docId;
                this.StoreDocumentElement(el);
            }
        }
        #endregion

        #region DeleteDocument
        public void DeleteDocument(int documentId)
        {
            this.ValidateToken();
            Document dbDocument = entities.Documents.Where(x => x.DocumentId.Equals(documentId)).FirstOrDefault();
            if (dbDocument != null)
            {
                List<int> ids = new List<int>();
                foreach (var item in dbDocument.DocumentElements)
                    ids.Add(item.DocumentElementId);
                foreach (var id in ids)
                    this.DeleteDocumentElement(id);
                entities.Documents.DeleteObject(dbDocument);
                entities.SaveChanges();
            }
            else
                this.Raise("Could not delete document");
        }
        #endregion

        #region CreateDocumentFromOrder
        public Model.Documents.Document CreateDocumentFromOrder(int orderId)
        {
            this.ValidateToken();
            Order dbOrder = entities.Orders.Where(x => x.OrderId.Equals(orderId)).FirstOrDefault();
            if (dbOrder != null)
            {
                Model.Documents.Document document = new Model.Documents.Document();
                if (dbOrder.Client != null)
                {
                    document.ClientCity = dbOrder.Client.City;
                    document.ClientFax = dbOrder.Client.Fax;
                    document.ClientInfo = dbOrder.Client.Info;
                    document.ClientName = dbOrder.Client.Name;
                    document.ClientName2 = dbOrder.Client.Name2;
                    document.ClientNip = dbOrder.Client.Nip;
                    document.ClientPhone = dbOrder.Client.Phone;
                    document.ClientStreet = dbOrder.Client.Street;
                    document.ClientZipcode = dbOrder.Client.Zipcode;
                    document.CreationDate = DateTime.Now;
                    document.IsPaid = false;
                    document.OrderId = orderId;
                    document.PaymentDate = DateTime.Now;
                    document.PaymentMethod = null;
                    document.DocumentType = null;
                }
                else
                {
                    Logger.LogError("Order has no client assigned");
                    return null;
                }

                if (dbOrder.OrderElements != null)
                {
                    List<Model.Documents.DocumentElement> items = new List<Model.Documents.DocumentElement>();
                    foreach (var dbItem in dbOrder.OrderElements)
                    {
                        if (dbItem.Product != null)
                        {
                            Model.Documents.DocumentElement item = new Model.Documents.DocumentElement
                            {
                                ProductName = dbItem.Product.Name,
                                ProductPrice = dbItem.Product.Price,
                                ProductSku = dbItem.Product.Sku,
                                ProductTaxValue = dbItem.Product.Tax.Value,
                                ProductUnitName = dbItem.Product.Unit.Name,
                                Spread = dbItem.Product.Spread,
                                Symb = dbItem.Product.Symb
                            };
                            if (dbItem.Product.Quantity >= dbItem.Quantity)
                            {
                                item.ProductQuantity = dbItem.Quantity;
                            }
                            else
                            {
                                Logger.LogError(string.Format("Product has limited quantity id:{0}", dbItem.ProductId));
                                return null;
                            }
                            items.Add(item);
                        }
                    }
                    document.Items = items;
                    return document;
                }
                else
                {
                    Logger.LogError("Could not get order elements");
                    return null;
                }
            }
            Logger.LogError("Could not find order");
            return null;
        }  
        #endregion

        #region Pay
        public void Pay(int documentId)
        {
            Document dbDocument = entities.Documents.Where(x => x.DocumentId.Equals(documentId)).FirstOrDefault();
            if (dbDocument != null)
            {
                dbDocument.IsPaid = true;
                entities.SaveChanges();
            }
            else
                this.Raise("Could not find document");
        } 
        #endregion

        #endregion

        #region Document element management

        #region GetDocumentElement
        public Model.Documents.DocumentElement GetDocumentElement(int documentElementId)
        {
            this.ValidateToken();
            DocumentElement dbElement = entities.DocumentElements.Where(x => x.DocumentElementId.Equals(documentElementId)).FirstOrDefault();
            if (dbElement != null)
            {
                Model.Documents.DocumentElement element = new Model.Documents.DocumentElement();
                this.FillDocumentElement(ref element, dbElement);
                return element;
            }
            Logger.LogError("Could not get document element");
            return null;
        }
        #endregion

        #region FillDocumentElement
        private void FillDocumentElement(ref Model.Documents.DocumentElement element, DocumentElement dbElement)
        {
            element.DocumentId = dbElement.DocumentId;
            element.Id = dbElement.DocumentElementId;
            element.ProductName = dbElement.ProductName;
            element.ProductPrice = dbElement.ProductPrice;
            element.ProductQuantity = dbElement.ProductQuantity;
            element.ProductSku = dbElement.ProductSku;
            element.ProductTaxValue = dbElement.ProductTaxValue;
            element.ProductUnitName = dbElement.ProductUnitName;
            element.Spread = dbElement.Spread;
            element.Symb = dbElement.Symb;
        }
        #endregion

        #region GetDocumentElementList
        public IEnumerable<Model.Documents.DocumentElement> GetDocumentElementList(int documentId)
        {
            this.ValidateToken();
            IEnumerable<DocumentElement> dbElements = entities.DocumentElements.Where(x => x.DocumentId.Equals(documentId));
            if (dbElements != null)
            {
                List<Model.Documents.DocumentElement> result = new List<Model.Documents.DocumentElement>();
                foreach (var dbElement in dbElements)
                {
                    Model.Documents.DocumentElement element = new Model.Documents.DocumentElement();
                    this.FillDocumentElement(ref element, dbElement);
                    result.Add(element);
                }
                return result;
            }
            Logger.LogError("Could not get document element list");
            return null;
        }
        #endregion

        #region StoreDocumentElement
        public void StoreDocumentElement(Model.Documents.DocumentElement documentElement)
        {
            this.ValidateToken();
            DocumentElement dbElement = null;
            Product product = entities.Products.Where(x => x.Sku.Equals(documentElement.ProductSku)).FirstOrDefault();
            if (product == null)
                this.Raise("Product for current document element could not be found");

            if (documentElement.Id != 0)
            {
                dbElement = entities.DocumentElements.Where(x => x.DocumentElementId.Equals(documentElement.Id)).FirstOrDefault();
                if (dbElement == null)
                    this.Raise("Could not update document element");

                if ((documentElement.ProductQuantity - dbElement.ProductQuantity) > product.Quantity)
                    this.Raise("Product has limited quantity");
                product.Quantity -= (documentElement.ProductQuantity - dbElement.ProductQuantity);
            }
            else
            {
                dbElement = new DocumentElement();
                if (documentElement.ProductQuantity > product.Quantity)
                    this.Raise("Product has limited quantity");
                product.Quantity -= documentElement.ProductQuantity;
            }
            dbElement.DocumentId = documentElement.DocumentId;
            dbElement.ProductName = documentElement.ProductName;
            dbElement.ProductPrice = documentElement.ProductPrice;
            dbElement.ProductQuantity = documentElement.ProductQuantity;
            dbElement.ProductSku = documentElement.ProductSku;
            dbElement.ProductTaxValue = documentElement.ProductTaxValue;
            dbElement.ProductUnitName = documentElement.ProductUnitName;
            dbElement.Spread = documentElement.Spread;
            dbElement.Symb = documentElement.Symb;
            if (documentElement.Id == 0)
            {
                entities.DocumentElements.AddObject(dbElement);
            }
            entities.SaveChanges();
        }
        #endregion 

        #region DeleteDocumentElement
        public void DeleteDocumentElement(int documentElementId)
        {
            this.ValidateToken();
            DocumentElement dbElement = entities.DocumentElements.Where(x => x.DocumentElementId.Equals(documentElementId)).FirstOrDefault();
            Product dbProduct = entities.Products.Where(x => x.Sku.Equals(dbElement.ProductSku)).FirstOrDefault();
            if (dbElement != null && dbProduct!=null)
            {
                dbProduct.Quantity += dbElement.ProductQuantity;
                entities.DocumentElements.DeleteObject(dbElement);
                entities.SaveChanges();
            }
            else
                this.Raise("Could not delete document element");
        } 
        #endregion

        #endregion

        #region Payment method management

        #region GetPaymentMethod
        public Model.Documents.PaymentMethod GetPaymentMethod(int paymentMethodId)
        {
            this.ValidateToken();
            PaymentMethod dbPaymentMeth = entities.PaymentMethods.Where(x => x.PaymentMethodId.Equals(paymentMethodId)).FirstOrDefault();
            if (dbPaymentMeth != null)
            {
                Model.Documents.PaymentMethod paymentMethod = new Model.Documents.PaymentMethod
                {
                    Id = paymentMethodId,
                    Name = dbPaymentMeth.Name
                };
                return paymentMethod;
            }
            Logger.LogError("Could not get payment method");
            return null;
        }
        #endregion

        #region GetPaymentMethodList
        public IEnumerable<Model.Documents.PaymentMethod> GetPaymentMethodList()
        {
            this.ValidateToken();
            IEnumerable<PaymentMethod> dbPaymentMeths = entities.PaymentMethods;
            if (dbPaymentMeths != null)
            {
                List<Model.Documents.PaymentMethod> result = new List<Model.Documents.PaymentMethod>();
                foreach (var dbPaymentMeth in dbPaymentMeths)
                {
                    Model.Documents.PaymentMethod paymentMethod = new Model.Documents.PaymentMethod
                    {
                        Id = dbPaymentMeth.PaymentMethodId,
                        Name = dbPaymentMeth.Name
                    };
                    result.Add(paymentMethod);
                }
                return result;
            }
            Logger.LogError("Could not get payment method list");
            return null;
        }
        #endregion

        #region StorePaymentMethod
        public void StorePaymentMethod(Model.Documents.PaymentMethod paymentMethod)
        {
            this.ValidateToken();
            PaymentMethod dbPaymentMeth = null;
            if (paymentMethod.Id != 0)
            {
                dbPaymentMeth = entities.PaymentMethods.Where(x => x.PaymentMethodId.Equals(paymentMethod.Id)).FirstOrDefault();
                if (dbPaymentMeth == null)
                    this.Raise("Could not update payment method");
            }
            else
            {
                dbPaymentMeth = new PaymentMethod();
            }
            dbPaymentMeth.Name = paymentMethod.Name;
            if (paymentMethod.Id == 0)
                entities.PaymentMethods.AddObject(dbPaymentMeth);
            entities.SaveChanges();
            this.NotifyAll(Util.ServiceType.DocumentService, "GetPaymentMethod", dbPaymentMeth.PaymentMethodId);
        }
        #endregion

        #region DeletePaymentMethod
        public void DeletePaymentMethod(int paymentMethodId)
        {
            this.ValidateToken();
            PaymentMethod dbPaymentMeth = entities.PaymentMethods.Where(x => x.PaymentMethodId.Equals(paymentMethodId)).FirstOrDefault();
            if (dbPaymentMeth != null)
            {
                entities.PaymentMethods.DeleteObject(dbPaymentMeth);
                entities.SaveChanges();
                this.NotifyAll(Util.ServiceType.DocumentService, "DeletePaymentMethod", paymentMethodId);
            }
            else
                this.Raise("Could not delete payment method");
        }
        #endregion 

        #endregion

        #region Document type management

        #region GetDocumentType
        public Model.Documents.DocumentType GetDocumentType(int documentTypeId)
        {
            this.ValidateToken();
            DocumentType dbType = entities.DocumentTypes.Where(x => x.DocumentTypeId.Equals(documentTypeId)).FirstOrDefault();
            if (dbType != null)
            {
                Model.Documents.DocumentType docType = new Model.Documents.DocumentType
                {
                    Id = dbType.DocumentTypeId,
                    Direction = (Model.Documents.DocumentDirection)Enum.Parse(typeof(Model.Documents.DocumentDirection), dbType.Direction),
                    Name = dbType.Name,
                    NextNo = dbType.NextNo,
                    Prefix = dbType.Prefix
                };
                return docType;
            }
            Logger.LogError("Could not get document type");
            return null;
        }
        #endregion

        #region GetDocumentTypeList
        public IEnumerable<Model.Documents.DocumentType> GetDocumentTypeList()
        {
            this.ValidateToken();
            IEnumerable<DocumentType> dbTypes = entities.DocumentTypes;
            if (dbTypes != null)
            {
                List<Model.Documents.DocumentType> result = new List<Model.Documents.DocumentType>();
                foreach (var dbType in dbTypes)
                {
                    Model.Documents.DocumentType docType = new Model.Documents.DocumentType
                    {
                        Id = dbType.DocumentTypeId,
                        Direction = (Model.Documents.DocumentDirection)Enum.Parse(typeof(Model.Documents.DocumentDirection), dbType.Direction),
                        Name = dbType.Name,
                        NextNo = dbType.NextNo,
                        Prefix = dbType.Prefix
                    };
                    result.Add(docType);
                }
                return result;
            }
            Logger.LogError("Could not get document type list");
            return null;
        }
        #endregion

        #region StoreDocumentType
        public void StoreDocumentType(Model.Documents.DocumentType documentType)
        {
            this.ValidateToken();
            DocumentType dbType = null;
            if (documentType.Id != 0)
            {
                dbType = entities.DocumentTypes.Where(x => x.DocumentTypeId.Equals(documentType.Id)).FirstOrDefault();
                if (dbType == null)
                {
                    this.Raise("Could not update document type");
                }
            }
            else
            {
                dbType = new DocumentType();
            }
            dbType.Direction = documentType.Direction.ToString();
            dbType.Name = documentType.Name;
            dbType.NextNo = documentType.NextNo;
            dbType.Prefix = documentType.Prefix;
            if (documentType.Id == 0)
            {
                entities.DocumentTypes.AddObject(dbType);
            }
            entities.SaveChanges();
            this.NotifyAll(Util.ServiceType.DocumentService, "GetDocumentType", dbType.DocumentTypeId);
        }
        #endregion

        #region DeleteDocumentType
        public void DeleteDocumentType(int documentTypeId)
        {
            this.ValidateToken();
            DocumentType dbDocumentType = entities.DocumentTypes.Where(x => x.DocumentTypeId.Equals(documentTypeId)).FirstOrDefault();
            if (dbDocumentType != null)
            {
                entities.DocumentTypes.DeleteObject(dbDocumentType);
                entities.SaveChanges();
                this.NotifyAll(Util.ServiceType.DocumentService, "DeleteDocumentType", documentTypeId);
            }
            else
                this.Raise("Could not delete document type");
        }
        #endregion 

        #endregion
    }
}
